package org.anyline.simiple;

import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.rendering.*;
import org.apache.pdfbox.text.*;
import java.awt.image.*;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.awt.geom.*;

public class PDFTableToHTML {
    // 配置参数
    private static final int DPI = 300;
    private static final int LINE_THRESHOLD = 180;
    private static final int MIN_LINE_LENGTH = 20;
    private static final double MERGE_THRESHOLD = 5.0;

    public static void main(String[] arg) {

        try {
            String html = new PDFTableToHTML().convert("d:\\order.pdf");
            Files.write(Paths.get("d:\\order.html"), html.getBytes());
        } catch (Exception e) {
            System.err.println("转换失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public String convert(String filePath) throws Exception {
        StringBuilder html = new StringBuilder("<!DOCTYPE html><html><head><meta charset='UTF-8'><style>");
        html.append("table { border-collapse: collapse; width: 100%; }");
        html.append("td { border: 1px solid #ddd; padding: 8px; }");
        html.append("</style></head><body>");

        try (PDDocument doc = PDDocument.load(new File(filePath))) {
            PDFRenderer renderer = new PDFRenderer(doc);
            for (int page = 0; page < doc.getNumberOfPages(); page++) {
                html.append(processPage(doc, renderer, page));
            }
        }
        return html.append("</body></html>").toString();
    }

    private String processPage(PDDocument doc, PDFRenderer renderer, int page) throws Exception {
        BufferedImage image = renderer.renderImageWithDPI(page, DPI);
        List<Cell> cells = detectCells(image);
        mergeAdjacentCells(cells);
        return generateHTML(doc, page, cells);
    }

    private List<Cell> detectCells(BufferedImage image) {
        List<Line> lines = new ArrayList<>();
        // 检测水平线
        for (int y = 0; y < image.getHeight(); y++) {
            Line line = scanLine(image, y, true);
            if (line != null) lines.add(line);
        }
        // 检测垂直线
        for (int x = 0; x < image.getWidth(); x++) {
            Line line = scanLine(image, x, false);
            if (line != null) lines.add(line);
        }
        return analyzeCells(lines);
    }

    private Line scanLine(BufferedImage image, int pos, boolean isHorizontal) {
        int start = -1;
        int length = isHorizontal ? image.getWidth() : image.getHeight();
        for (int i = 0; i < length; i++) {
            int x = isHorizontal ? i : pos;
            int y = isHorizontal ? pos : i;
            if (isLinePixel(image, x, y)) {
                if (start == -1) start = i;
            } else if (start != -1) {
                if (i - start > MIN_LINE_LENGTH) {
                    return isHorizontal ?
                            new Line(start, pos, i, pos) :
                            new Line(pos, start, pos, i);
                }
                start = -1;
            }
        }
        return null;
    }

    private boolean isLinePixel(BufferedImage image, int x, int y) {
        if (x < 0 || y < 0 || x >= image.getWidth() || y >= image.getHeight())
            return false;
        int rgb = image.getRGB(x, y);
        int r = (rgb >> 16) & 0xFF;
        int g = (rgb >> 8) & 0xFF;
        int b = rgb & 0xFF;
        return (r + g + b) / 3 < LINE_THRESHOLD;
    }

    private List<Cell> analyzeCells(List<Line> lines) {
        TreeSet<Double> xCoords = new TreeSet<>();
        TreeSet<Double> yCoords = new TreeSet<>();
        for (Line line : lines) {
            if (line.isVertical()) {
                xCoords.add(line.x1);
            } else {
                yCoords.add(line.y1);
            }
        }
        // 添加边界坐标
        if (!xCoords.isEmpty()) xCoords.add(xCoords.last() + 100);
        if (!yCoords.isEmpty()) yCoords.add(yCoords.last() + 100);

        List<Cell> cells = new ArrayList<>();
        Double[] xArray = xCoords.toArray(new Double[0]);
        Double[] yArray = yCoords.toArray(new Double[0]);

        for (int row = 0; row < yArray.length - 1; row++) {
            for (int col = 0; col < xArray.length - 1; col++) {
                cells.add(new Cell(
                        xArray[col], yArray[row],
                        xArray[col+1] - xArray[col],
                        yArray[row+1] - yArray[row],
                        row, col
                ));
            }
        }
        return cells;
    }

    private void mergeAdjacentCells(List<Cell> cells) {
        // 按行分组
        Map<Integer, List<Cell>> rowMap = new HashMap<>();
        for (Cell cell : cells) {
            rowMap.computeIfAbsent(cell.row, k -> new ArrayList<>()).add(cell);
        }

        // 水平合并
        for (List<Cell> row : rowMap.values()) {
            row.sort(Comparator.comparingInt(c -> c.col));
            for (int i = 0; i < row.size() - 1; i++) {
                Cell current = row.get(i);
                Cell next = row.get(i + 1);
                if (Math.abs(current.x + current.width - next.x) < MERGE_THRESHOLD) {
                    current.width += next.width;
                    current.colspan++;
                    next.merged = true;
                }
            }
        }

        // 垂直合并
        for (int col = 0; ; col++) {
            List<Cell> column = new ArrayList<>();
            for (Cell cell : cells) {
                if (cell.col == col && !cell.merged) column.add(cell);
            }
            if (column.isEmpty()) break;

            column.sort(Comparator.comparingInt(c -> c.row));
            for (int i = 0; i < column.size() - 1; i++) {
                Cell current = column.get(i);
                Cell next = column.get(i + 1);
                if (Math.abs(current.y + current.height - next.y) < MERGE_THRESHOLD) {
                    current.height += next.height;
                    current.rowspan++;
                    next.merged = true;
                }
            }
        }
    }

    private String generateHTML(PDDocument doc, int page, List<Cell> cells) throws Exception {
        StringBuilder html = new StringBuilder("<table>");
        PDFTextStripperByArea stripper = new PDFTextStripperByArea();
        stripper.setSortByPosition(true);

        // 按行分组
        Map<Integer, List<Cell>> rowMap = new TreeMap<>();
        for (Cell cell : cells) {
            if (!cell.merged) {
                rowMap.computeIfAbsent(cell.row, k -> new ArrayList<>()).add(cell);
            }
        }

        // 生成表格
        for (List<Cell> row : rowMap.values()) {
            html.append("<tr>");
            for (Cell cell : row) {
                stripper.addRegion("cell", cell.toRectangle());
                stripper.extractRegions(doc.getPage(page));
                String content = stripper.getTextForRegion("cell").trim();

                html.append("<td");
                if (cell.colspan > 1) html.append(" colspan='").append(cell.colspan).append("'");
                if (cell.rowspan > 1) html.append(" rowspan='").append(cell.rowspan).append("'");
                html.append(">").append(content).append("</td>");
            }
            html.append("</tr>");
        }
        return html.append("</table>").toString();
    }

    private static class Line {
        final double x1, y1, x2, y2;
        Line(double x1, double y1, double x2, double y2) {
            this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;
        }
        boolean isVertical() { return Math.abs(x1 - x2) < 5; }
    }

    private static class Cell {
        final double x;
        final double y;
        double width;
        double height;
        final int row, col;
        int colspan = 1;
        int rowspan = 1;
        boolean merged = false;

        Cell(double x, double y, double w, double h, int row, int col) {
            this.x = x; this.y = y; this.width = w; this.height = h;
            this.row = row; this.col = col;
        }

        Rectangle2D.Double toRectangle() {
            return new Rectangle2D.Double(x, y, width, height);
        }
    }
}
